use crate::init::_mi_process_is_initialized;
use crate::mimalloc::{types::*, atomic::*, internal::*};
use crate::{heap, mi_assert, page_queue};
use crate::segment::{mi_segment_page_start,mi_segment_page_alloc};
use crate::

impl MiPage {
    pub fn mi_page_blcok_at(self, page_start: *mut u8, i: usize ) ->*mut MiBlock{
        (page_start as usize + (i * self.block_size)) as *mut MiBlock
    } 

    pub fn mi_page_list_is_valid(&self, mut p: *mut MiBlock) -> bool {
        let mut psize: usize = 0;
        let page_area = mi_segment_page_start(std::ptr::null::<MiSegment>() , self, &mut psize);
        let start = page_area as *mut MiBlock;
        let end = unsafe {page_area.add(psize) as *mut MiBlock};

        while !p.is_null() {
            if p < start || p >= end {
                return false;
            }
            p = mi_block_next(self, p);
        }

        if self.free_is_zero == 1{
            let ubsize = self.mi_page_usable_block_size();
            let mut block = self.free;
            while !block.is_null() {
                let block_data = unsafe {block.add(1)};
                if !mi_mem_is_zero(block_data as *const _, ubsize - std::mem::size_of::<MiBlock>()) {
                    return false;
                }
                block = mi_block_next(self, block);
            }
        }
        true
    }
    
    pub fn mi_page_list_count (&self, mut head: *mut MiBlock) -> usize {
        let mut count = 0;
        while head!= std::ptr::null_mut() {
            count += 1;
            head = mi_block_next(self, head);
        }
        count
    }
    
    pub fn mi_page_is_valid_init(&self ) -> bool {
        mi_assert!(self.mi_page_block_size()!=0);
        mi_assert!(self.used <= self.capacity);
        mi_assert!(self.capacity <= self.reserved);

        let start = self.mi_page_start();
        mi_assert!(start == mi_segment_page_start(mi_page_segment(self), self, &mut 0));
        unsafe{
            mi_assert!((self.is_huge != 0) == ((&mut *mi_page_segment(self)).kind == MiSegmentKind::MI_SEGMENT_HUGE) );
        }

        if MI_DEBUG > 3 {
            if self.free_is_zero != 0 {
                let ubsize = self.mi_page_usable_block_size();
                let mut block = self.free;
                while !block.is_null() {
                    mi_assert!(mi_mem_is_zero(block.wrapping_add(1) as *const _, ubsize - std::mem::size_of::<MiBlock>()));
                    block = mi_block_next(self, block);
                }
            }
        }

        let free_count = self.mi_page_list_count( self.free);
        mi_assert!(self.used as usize + free_count == self.capacity as usize);
        true
    }

    pub fn mi_page_is_valid(&self) -> bool {
        mi_assert!(self.mi_page_is_valid());
        if MI_SECURE != 0 {
            mi_assert!(self.keys[0] != 0);
        }
        if mi_page_heap(self).is_some() {
            let segment = mi_page_segment(self);
            mi_assert!(!_mi_process_is_initialized);
            unsafe {
                if (&mut *segment).kind == MiSegmentKind::MI_SEGMENT_HUGE {
                    let pq = self.clone().mi_page_queue_of();
                    if let Some(pq) = pq {
                        mi_assert!(pq.as_ref().borrow().clone().mi_page_queue_contains(self.clone().to_option_rc_refcell()));
                    }
                }
            }
        }
        true
    }

}
pub fn mi_page_use_delayed_free(page:&mut MiPage , delay: MiDelayed, override_never: bool ){
    
}

pub fn mi_page_try_use_delayed_free(page:&mut MiPage , delay: MiDelayed, override_never: bool ) -> bool {
    let mut yield_count = 0;
    loop {
        let mut tfree = mi_atomic_load_acquire(&page.xthread_free);
        let tfreex = mi_tf_set_delayed(tfree, delay);
        let old_delay = mi_tf_delayed(tfree);

        if old_delay == MiDelayed::MI_DELAYED_FREEING {
            if yield_count >= 4 {
                return false;
            }
            yield_count += 1;
            mi_atomic_yield(); 
        } else if delay == old_delay {
            break; 
        } else if !override_never && old_delay == MiDelayed::MI_NEVER_DELAYED_FREE {
            break; 
        }

        if old_delay == MiDelayed::MI_DELAYED_FREEING || 
        match mi_atomic_cas_weak_release(&page.xthread_free, &mut tfree, tfreex) {
            Ok(_) => false, 
            Err(_) => true, 
        } {
            continue;
        }


        break; 
    }

    return true;
}

impl MiPage {
    pub fn mi_page_thread_free_collect(&mut self){
        let mut head: *mut MiBlock;
        let mut tfree: MiThreadFree = mi_atomic_load_relaxed(&self.xthread_free);
        
        loop {
            head = mi_tf_block(tfree);
            let tfreex = mi_tf_set_block(tfree, std::ptr::null_mut());
            
            if let Ok(_) = mi_atomic_cas_weak_acq_rel(&mut self.xthread_free, &mut tfree, tfreex) {
                break;
            }
            tfree = mi_atomic_load_relaxed(&self.xthread_free);
        }
    
        if head.is_null() {
            return;
        }
    
        let max_count = self.capacity; 
        let mut count: usize = 1;
        let mut tail = head;
        let next = mi_block_next(self, head);

        while !next.is_null() && count <= max_count as usize {
            count+=1;
            tail = next;
        }

        if count > max_count as usize {
            return;
        }

        mi_block_set_next(self, tail, self.local_free);
        self.local_free = head;
        self.used -= count as u16;
    }

    pub fn mi_page_free_collect (&mut self, force: bool){
        if force || self.mi_page_thread_free() != std::ptr::null_mut() {
            self.mi_page_thread_free_collect();
        }
        if self.local_free != std::ptr::null_mut() {
            if self.free == std::ptr::null_mut() {
                self.free = self.local_free;
                self.local_free = std::ptr::null_mut();
                self.free_is_zero = 0;
            }
            else if force {
                let mut tail = self.local_free;
                let next = mi_block_next(self, tail);
                while next != std::ptr::null_mut() {
                    tail = next;
                }
                mi_block_set_next(self, tail, self.free);
                self.free = self.local_free;
                self.local_free = std::ptr::null_mut();
                self.free_is_zero = 0;
    
            }
        }
        mi_assert!(!force || self.local_free == std::ptr::null_mut());
    }
    
}

/* -----------------------------------------------------------
  Page fresh and retire
----------------------------------------------------------- */
pub fn mi_page_reclaim(mut heap: MiHeap, page: &mut MiPage) {
    mi_assert!(page.mi_page_is_valid_init());
    mi_assert !(page.mi_page_thread_free_flag() ==  MiDelayed::MI_NEVER_DELAYED_FREE);
    let pq = mi_page_queue(heap, page.mi_page_block_size());
    let pq_mut = unsafe { &mut *(pq as *mut _) };  
    heap.mi_page_queue_push(pq_mut, page);
}

pub fn mi_page_fresh_alloc(heap: &mut MiHeap, pq: &mut MiPageQueue,block_size: usize,page_alignment: usize) -> Option<Rc<RefCell<MiPage>>> {
    // todo in ssegment.rs
}


